#include "MapManager.h"
#include "drogon/drogon.h"
#include "OCMain/Environment.h"
#include "OCMain/FileUtil.h"
#include "OCMain/Log.h"
#include "ormImp/FileInfo.h"
#include "ormImp/FileData.h"
#include "MapCacher.h"

using namespace drogon;
using namespace drogon::orm;
using namespace drogon_model;

namespace OCPP
{
	template<> MapManager* CSingleton<MapManager>::msSingleton = 0;

	inline String findOne(DbClientPtr dbClient, String path)
	{
		try {
			Mapper<sqlite3::FileInfo> mp(dbClient);
			auto result = mp.findOne(Criteria(sqlite3::FileInfo::Cols::_file_path, CompareOperator::EQ, path));
			auto data = result.getData(dbClient);
			return data.getValueOfFileDataAsString();
		}
		catch (...) {}

		return "";
	}

	MapManager::~MapManager()
	{
		MapCacher::shutdown();
	}

	MapManager* MapManager::getSingletonPtr(void)
	{
		return msSingleton;
	}

	MapManager& MapManager::getSingleton(void)
	{
		assert(msSingleton);  return (*msSingleton);
	}

	String MapManager::getFileName(String layer)
	{
		auto it = _strFile.find(layer);
		if (it != _strFile.end())
		{
			return it->second;
		}
		return "";
	}

	String MapManager::getExt(String layer)
	{
		auto it = _strExt.find(layer);
		if (it != _strExt.end())
		{
			return it->second;
		}
		return "";
	}

	String MapManager::getMvt(String layer, int z, int y, int x)
	{
		auto it = _strPath.find(layer);
		if (it != _strPath.end())
		{
			String path = it->second;
			String name = Stringify() << y << ".pbf";
			String fileName = Stringify() << path << "/" << z << "/" << x << "/" << name;
			return FileUtil::getFileBinary(fileName);
		}

		return "";
	}

	String MapManager::getMap(String layer, int z, int y, int x)
	{
		auto it = _strExt.find(layer);
		if (it != _strExt.end())
		{
			if (_strFile.find(layer) != _strFile.end())
			{
				auto dbClient = drogon::app().getDbClient(layer);
				if (dbClient)
				{
					String ext = it->second;
					if (!ext.empty()) ext.insert(ext.begin(), '.');
					String path = Stringify() << z << "/" << y << "/" << x << ext;
					String data = findOne(dbClient, path);
					if (data.empty())
					{
						data = cacheIf(layer, z, y, x);
					}
					return data;
				}
			}
			else
			{
				auto it = _mapCacherSet.find(layer);
				if (it != _mapCacherSet.end())
				{
					return it->second->getCacheSync(layer, z, y, x);
				}
			}
		}

		return "";
	}

	bool MapManager::registerMap(const Json::Value& config)
	{
		String fileName = config["path"].asString();
		String ext = config["ext"].asString();
		bool bRegisterCacher = false;
		bool bRegisterMap = false;

		const auto& cacherConf = config["cache"];
		if (!cacherConf.isNull())
		{
			registerCacher(fileName, ext, cacherConf);
			bRegisterCacher = true;
		}

		bRegisterMap = registerMap(fileName, ext);
		
		if (!bRegisterCacher)
		{
			if (!bRegisterMap)
			{
				LOG_ERROR << fileName << " not found.\n";
			}
			else
			{
				LOG_INFO << fileName << " register map.\n";
			}
		}


		return true;
	}

	bool MapManager::registerMap(String fileName, String ext)
	{
		if (!FileUtil::existFile(fileName))
		{
			return false;
		}

		String name, ext1, path;
		StringUtil::splitFullFilename(fileName, name, ext1, path);

		if (FileUtil::typeOfFile(fileName) == FileUtil::DIRECTORY)
		{
			_strPath[name] = fileName;
		}
		else
		{
			drogon::app().createDbClient("sqlite3", "", 0, "", "", "", 1, fileName, name);
			_strExt[name] = ext;
			_strFile[name] = fileName;
		}

		return true;
	}

	String MapManager::cacheIf(String layer, String path)
	{
		auto it = _mapCacherSet.find(layer);
		if (it == _mapCacherSet.end())
		{
			return "";
		}

		return it->second->getCache(layer, path);
	}

	String MapManager::cacheIf(String layer, int z, int y, int x)
	{
		auto it = _mapCacherSet.find(layer);
		if (it == _mapCacherSet.end())
		{
			return "";
		}

		return it->second->getCache(layer, z, y, x);
	}

	bool MapManager::registerCacher(String fileName, String ext, String url, String path, StringVector subdomains)
	{
		if (fileName.empty() || url.empty()) return false;
		String name, ext1, path1;
		StringUtil::splitFullFilename(fileName, name, ext1, path1);
		_mapCacherSet[name] = std::make_shared<MapCacher>(url, path, ext, subdomains);
		_strExt[name] = ext;
		return true;
	}

	void MapManager::registerCacher(String fileName, String ext, const Json::Value& conf)
	{
		String name, ext1, path1;
		StringUtil::splitFullFilename(fileName, name, ext1, path1);

		String url = conf["url"].asString();
		String path = conf["path"].asString();

		StringVector subdomains;

		const auto& dmainsObj = conf["subdomains"];
		if (dmainsObj.size() > 0)
		{
			for (auto i = 0; i < dmainsObj.size(); i++)
			{
				subdomains.push_back(dmainsObj[i].asString());
			}
		}

		_mapCacherSet[name] = std::make_shared<MapCacher>(url, path, ext, subdomains);
		_strExt[name] = ext;
		LOG_INFO << fileName << " register map cache " << path << "\n";
	}

	String MapManager::get3dtiles(String layer, String path)
	{
		auto it = _strExt.find(layer);
		if (it != _strExt.end())
		{
			if (_strFile.find(layer) != _strFile.end())
			{
				auto dbClient = drogon::app().getDbClient(layer);
				if (dbClient)
				{
					String ext = it->second;
					if (!ext.empty()) ext.insert(ext.begin(), '.');
					String data = cacheIf(layer, path);
					return data;
				}
			}
			else
			{
				auto it = _mapCacherSet.find(layer);
				if (it != _mapCacherSet.end())
				{
					return it->second->getCacheSync(layer, path);
				}
			}
		}

		return "";
	}

}
